/**
 * (c) Copyright Ascensio System SIA 2025
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zzvcom.office.controllers;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.onlyoffice.manager.request.RequestManager;
import com.onlyoffice.manager.security.JwtManager;
import com.onlyoffice.manager.settings.SettingsManager;
import com.onlyoffice.manager.url.UrlManager;
import com.onlyoffice.model.commandservice.CommandRequest;
import com.onlyoffice.model.commandservice.CommandResponse;
import com.onlyoffice.model.commandservice.commandrequest.Command;
import com.onlyoffice.model.commandservice.commandrequest.Meta;
import com.onlyoffice.model.common.Format;
import com.onlyoffice.model.convertservice.ConvertRequest;
import com.onlyoffice.model.convertservice.ConvertResponse;
import com.onlyoffice.model.documenteditor.Callback;
import com.onlyoffice.service.command.CommandService;
import com.onlyoffice.service.convert.ConvertService;
import com.onlyoffice.service.documenteditor.callback.CallbackService;
import com.zzvcom.office.documentserver.managers.history.HistoryManager;
import com.zzvcom.office.documentserver.storage.FileStorageMutator;
import com.zzvcom.office.documentserver.storage.FileStoragePathBuilder;
import com.zzvcom.office.dto.*;
import com.zzvcom.office.dto.RefreshConfig.Document;
import com.zzvcom.office.dto.RefreshConfig.EditorConfig;
import com.zzvcom.office.entities.OfficeFile;
import com.zzvcom.office.entities.OfficePdf;
import com.zzvcom.office.entities.User;
import com.zzvcom.office.exception.OfficeException;
import com.zzvcom.office.repositories.OfficeFileRepository;
import com.zzvcom.office.sdk.manager.DocumentManager;
import com.zzvcom.office.sdk.service.ConfigService;
import com.zzvcom.office.services.DownloadServices;
import com.zzvcom.office.services.OfficeFileServices;
import com.zzvcom.office.services.OfficePdfServices;
import com.zzvcom.office.services.UserServices;
import com.zzvcom.office.util.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.core5.http.HttpEntity;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;

@CrossOrigin("*")
@Controller
@Slf4j
public class FileController {

    @Autowired
    private JwtManager jwtManager;
    @Autowired
    private FileStorageMutator storageMutator;
    @Autowired
    private FileStoragePathBuilder storagePathBuilder;
    @Autowired
    private UserServices userService;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private HistoryManager historyManager;
    @Autowired
    private DocumentManager documentManager;
    @Autowired
    private ConvertService convertService;
    @Autowired
    private RequestManager requestManager;
    @Autowired
    private SettingsManager settingsManager;
    @Autowired
    private CallbackService callbackService;
    @Autowired
    private CommandService commandService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private UrlManager urlManager;

    @Autowired
    private OfficeFileServices officeFileServices;
    @Autowired
    private MyFileUtil myFileUtil;
    @Autowired
    private UserMetadataUtil userMetadataUtil;
    @Autowired
    private OfficePdfServices officePdfServices;
    @Autowired
    private DownloadServices downloadServices;
    @Autowired
    private LimitUtil limitUtil;
    /**
     * 下载文件地址
     *
     * @param fileId 文件编码
     * @return
     * @throws MalformedURLException
     */
    private ResponseEntity<Resource> downloadFile(final String fileId) throws MalformedURLException {
        OfficeFile officeFile = officeFileServices.getById(fileId);
        if (officeFile == null) {
            throw new RuntimeException("文件不存在,fileId=" + fileId);
        }
        Resource resource = null;// load the specified file as a resource
        //本地的地址
        if (OfficeFile.LOCAL_FLAG_YES.equals(officeFile.getLocalFlag())) {
            resource = storageMutator.loadFileAsResource(officeFile, officeFile.getName());  // load the specified file as a resource
        } else {
            resource = new UrlResource(officeFile.getDownloadUrl());


        }
        String contentType = "application/octet-stream";
        //下载文件名称使用title
        String encodedFileName = CommonUtil.encodeFileName(officeFile.getTitle());
        // create a response with the content type, header and body with the file data
        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType(contentType))
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName)
                .body(resource);
    }

    /**
     * 下载文件地址
     *
     * @param fileId 文件编码
     * @return
     * @throws MalformedURLException
     */
    private ResponseEntity<Resource> downloadFilePdf(final String fileId,int ct) throws MalformedURLException {
        OfficePdf officePdf = officePdfServices.getById(fileId);
        if (officePdf == null) {
            throw new RuntimeException("文件不存在,fileId=" + fileId);
        }
        return downloadServices.getResourceResponseEntity(ct, officePdf);
    }

    private ResponseEntity<Resource> downloadSample(final String fileName) {
        String serverPath = System.getProperty("user.dir");
        String contentType = "application/octet-stream";
        String[] fileLocation = new String[]{serverPath, "src", "main", "resources", "assets", "document-templates",
                "sample", fileName};
        Path filePath = Paths.get(String.join(File.separator, fileLocation));
        Resource resource;
        try {
            resource = new UrlResource(filePath.toUri());
            if (resource.exists()) {
                return ResponseEntity.ok()
                        .contentType(MediaType.parseMediaType(contentType))
                        .header(HttpHeaders.CONTENT_DISPOSITION,
                                "attachment; filename=\"" + resource.getFilename() + "\"")
                        .body(resource);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return null;
    }

    // download data from the specified history file
    private ResponseEntity<Resource> downloadFileHistory(final String fileName,
                                                         final String version,
                                                         final String file) {

        // load the specified file as a resource
        Resource resource = storageMutator.loadFileAsResourceHistory(fileName, version, file);
        String contentType = "application/octet-stream";
        // create a response with the content type, header and body with the file data
        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType(contentType))
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(resource);
    }

    /**
     * 处理文件上传请求
     *
     * @param file 上传的文件对象
     * @param uid  用户ID，从Cookie中获取
     * @return 上传结果的JSON字符串，包含用户元数据或错误信息
     */
    @PostMapping("/upload")
    @ResponseBody
    public String upload(@RequestParam("file") final MultipartFile file,  // upload a file
                         @CookieValue("uid") final String uid) {
        limitUtil.validateOp("upload");
        try {
            String fullFileName = file.getOriginalFilename();  // get file name
          /*  long fileSize = file.getSize();  // get file size
            // 验证文件大小是否符合要求
            if (documentManager.getMaxFileSize() < fileSize || fileSize <= 0) {
                return "{ \"error\": \"File size is incorrect\"}";  // if so, write an error message to the response
            }*/
            // 验证文件类型是否受支持
            if (documentManager.getDocumentType(fullFileName) == null) {
                // if not, write an error message to the response
                return "{ \"error\": \"File type is not supported\"}";
            }
            OfficeFile officeFile = storageMutator.updateFile(file);  // update a file
            if (officeFile == null) {
                throw new IOException("上传文件失败");  // if the file cannot be updated, an error occurs
            }
            //上传成功
            return userMetadataUtil.createUserMetadata(uid, officeFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // if the operation of file uploading is unsuccessful, an error occurs
        return "{ \"error\": \"Something went wrong when uploading the file.\"}";
    }


    /**
     * 处理文件转换请求，将指定文件根据参数转换为目标格式，并保存转换后的文件。
     *
     * @param body 包含文件转换所需信息的请求体对象，包括文件名、密码、目标扩展名等
     * @param uid  用户ID，从Cookie中获取
     * @param lang 用户语言设置，从Cookie中获取
     * @return 转换结果的JSON字符串，可能包含错误信息或新文件的元数据
     */
    @PostMapping(path = "${url.converter}")
    @ResponseBody
    public String convert(@RequestBody final Converter body,  // convert a file
                          @CookieValue("uid") final String uid, @CookieValue("ulang") final String lang) {
        limitUtil.validateOp("convert");
        log.info("Converter body=" + body);
        String fileId = body.getFileId();
        // 获取文件密码（如果存在）
        String filePass = body.getFilePass() != null ? body.getFilePass() : null;
        // 获取目标转换格式扩展名，若未提供则默认为 ooxml
        String conversionExtension = body.getFileExt() != null ? body.getFileExt() : "ooxml";
        // 判断是否保留原始文件，默认为 false
        Boolean keepOriginal = body.getKeepOriginal() != null ? body.getKeepOriginal() : false;
        OfficeFile officeFile = officeFileServices.getById(body.getFileId());
        if (officeFile == null) {
            throw OfficeException.valueOf("文件不存在,fileId=" + body.getFileId());
        }
        // 获取原始文件名
        String fileName = officeFile.getName();
        String title=officeFile.getTitle();
        try {
            // 检查该文件是否支持转换
            if (documentManager.getDefaultConvertExtension(fileName) != null || body.getFileExt() != null) {
                // 构建转换请求参数
                ConvertRequest convertRequest = ConvertRequest.builder()
                        .password(filePass)
                        .outputtype(conversionExtension)
                        .region(lang)
                        .async(true)
                        .title(title)
                        .build();
                log.info("convertRequest={}", convertRequest);
                // 执行文件转换操作
                ConvertResponse convertResponse = convertService.processConvert(convertRequest, fileId);
                String s = objectMapper.writeValueAsString(convertResponse);
                log.info("convertResponse={}", s);
                // 若转换过程中出现错误或未返回有效文件链接，则直接返回响应内容
                if (convertResponse.getError() != null || convertResponse.getFileUrl() == null) {
                    return s;
                }
                // 获取转换后文件的URL和类型
                String newFileUri = convertResponse.getFileUrl();
                String newFileType = convertResponse.getFileType();
                log.info("newFileUri={},newFileType={}", newFileUri, newFileType);
                List<Format> formats = documentManager.getFormats();
                log.info(objectMapper.writeValueAsString(formats));
                // 验证转换后的文件类型是否在支持的格式列表中
                if (!new FormatsList(formats).getFormats().stream().anyMatch(
                        f -> newFileType.equals(f.getName()) && f.getType() != null)
                ) {
                    return "{ \"percent\" : \"100\", \"filename\" : \"" + newFileUri
                            + "\", \"error\":\"FileTypeIsNotSupported\"}";
                }

                /* 根据转换后的文件名生成带内部扩展名的新文件名，
                   如果该文件名已存在，则自动添加索引以避免冲突 */
                final String oldFileName = fileName;
                String nameWithInternalExt = documentManager.getBaseName(fileName) + "." + newFileType;
                String correctedName = documentManager.getCorrectName(officeFile, nameWithInternalExt);

                // 下载并存储转换后的文件
              requestManager.executeGetRequest(newFileUri, response -> {
                    // 获取转换后文件的输入流
                    InputStream stream = ((HttpEntity) response).getContent();

                    if (stream == null) {
                        throw new RuntimeException("Input stream is null");
                    }

                    // 若不保留原始文件，则删除原始文件
                    if (!keepOriginal) {
                        storageMutator.deleteFile(officeFile, oldFileName);
                    }

                    // 将转换后的文件写入存储系统
                    storageMutator.createFileNotExists(Path.of(storagePathBuilder.getFileLocation(officeFile, correctedName)), stream);
                    return correctedName;
                });
                // 创建用户对转换后文件的元数据记录
                long size = Files.size(Path.of(storagePathBuilder.getFileLocation(officeFile, correctedName)));
                officeFile.setName(correctedName);
                officeFile.setNewFileSize(size);
                officeFile.setNewExt(newFileType);
                officeFile.setFileKey(IdUtil.getFileKey(fileId));
                officeFile.setTitle(title+"."+newFileType);
                officeFileServices.save(officeFile);
                // 创建用户对转换后文件的元数据记录
                return userMetadataUtil.createUserMetadata(uid, officeFile);
            } else {
                return "{ \"error\": \"FileTypeIsNotSupported\"}";
            }

        } catch (Exception e) {
            e.printStackTrace();
            // 返回转换过程中的异常信息
            return "{ \"error\": \"" + e.getMessage() + "\"}";
        }

    }


    /**
     * 删除文件或用户文件夹
     *
     * @param body 包含要删除的文件名的转换器对象
     * @return 返回操作结果的JSON字符串，包含success字段表示操作是否成功，或error字段表示错误信息
     */
    @PostMapping("/delete")
    @ResponseBody
    public String delete(@RequestBody final Converter body) {  // delete a file
        limitUtil.validateOp("delete");

        if (body.getFileId() == null) {
            throw OfficeException.valueOf("文件ID不能为空");
        }
        /*try {
            String filename = body.getFileName();
            boolean success = false;

            if (filename != null) {
                // 获取完整文件名
                String fullFileName = documentManager.getDocumentName(filename);

                // 删除存储中的文件并获取操作状态
                boolean fileSuccess = storageMutator.deleteFile(fullFileName);

                // 删除文件历史记录并获取操作状态
                boolean historySuccess = storageMutator.deleteFileHistory(fullFileName);
                success = fileSuccess && historySuccess;
            } else {
                // 删除用户文件夹并获取操作状态
                success = storageMutator.deleteUserFolder();
            }
            return "{ \"success\": \"" + (success) + "\"}";
        } catch (Exception e) {
            return "{ \"error\": \"" + e.getMessage() + "\"}";
        }*/
        return null;
    }


    /**
     * 下载历史文件接口 暂时这样吧。
     *
     * @param request  HTTP请求对象，用于获取请求头信息
     * @param fileName 文件名参数，fileName
     * @param version  版本参数，指定文件的版本信息
     * @param file     文件参数，指定具体的历史文件路径
     * @return ResponseEntity<Resource> 响应实体，包含下载的文件资源
     */
    @GetMapping("/downloadhistory")
    public ResponseEntity<Resource> downloadHistory(final HttpServletRequest request, // download a file
                                                    @RequestParam("fileName") final String fileName,
                                                    @RequestParam("ver") final String version,
                                                    @RequestParam("file") final String file) { // history file
        limitUtil.validateOp("downloadhistory");
        try {
            // 安全验证：检查是否启用了安全令牌验证
            if (settingsManager.isSecurityEnabled()) {
                String header = request.getHeader(settingsManager.getSecurityHeader());
                if (header != null && !header.isEmpty()) {
                    String token = header
                            .replace("Bearer ", "");  // token is the header without the Bearer prefix
                    jwtManager.verify(token);  // read the token
                } else {
                    return null;
                }
            }
            // 执行文件下载操作
            return downloadFileHistory(fileName, version, file);  // download data from the specified file
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 下载文件接口,修改完毕
     *
     * @param request     HTTP请求对象，用于获取请求头信息
     * @param fileId      要下载的文件编码
     * @param userAddress 用户地址，可选参数
     * @return ResponseEntity<Resource> 文件资源响应实体
     */
    @GetMapping(path = "${url.download}")
    public ResponseEntity<Resource> download(final HttpServletRequest request,  // download a file
                                             @RequestParam("fileId") final String fileId,
                                             @RequestParam(value = "userAddress", required = false) final String userAddress) {
        try {
            // 验证安全令牌
            if (settingsManager.isSecurityEnabled() && userAddress != null) {
                String header = request.getHeader(settingsManager.getSecurityHeader());
                if (header != null && !header.isEmpty()) {
                    String token = header
                            .replace("Bearer ", "");  // token is the header without the Bearer prefix
                    jwtManager.verify(token);  // read the token
                } else {
                    return null;
                }
            }
            return downloadFile(fileId);  // download data from the specified file
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 下载PDF文件接口
     * @param request
     * @param fileId 文件id
     * @param ct 0是播放，1是下载
     * @return
     */
    @GetMapping(path = "${url.downloadpdf}")
    public ResponseEntity<Resource> downloadPdf(final HttpServletRequest request,  // download a file
                                                @RequestParam("fileId") final String fileId,
    int ct) {
        try {
            return downloadFilePdf(fileId,ct);  // download data from the specified file
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 新建文件修改完毕
     *
     * @param fileExt  新建文件类型的扩展名字
     * @param isSample 是否是样例文件。
     * @param uid      用户账号
     * @param model
     * @return
     */
    @GetMapping("/create")
    public String create(@RequestParam("fileExt") final String fileExt, // create a sample file of the specified extension
                         @RequestParam(value = "sample", required = false) final Optional<Boolean> isSample,
                         @CookieValue(value = "uid", required = false) final String uid,
                         final Model model) {
        limitUtil.validateOp("create");
        // specify if the sample data exists or not
        Boolean sampleData = (isSample.isPresent() && !isSample.isEmpty()) && isSample.get();
        if (fileExt != null) {
            try {
                User user = userService.findUserById(Integer.parseInt(uid));  // find a user by their ID
                if (user==null) {
                    // if the user with the specified ID doesn't exist, an error occurs
                    throw new RuntimeException("Could not fine any user with id = " + uid);
                }
                OfficeFile officeFile = documentManager.createDemo(fileExt,
                        sampleData,
                        uid,
                        user.getName());  // create a demo document with the sample data
                if (officeFile == null) {
                    throw new RuntimeException("You must have forgotten to add asset files");
                }
                return "redirect:editor?action=edit&fileId=" + officeFile.getId();  // redirect the request
            } catch (Exception ex) {
                model.addAttribute("error", ex.getMessage());
                return "error.html";
            }
        }
        return "redirect:/";
    }

    @GetMapping("/assets")
    public ResponseEntity<Resource> assets(@RequestParam("name") final String name) {  // get sample files from the assests
        return downloadSample(name);
    }

    @GetMapping("/csv")
    public ResponseEntity<Resource> csv() {  // download a csv file
        return downloadSample("csv.csv");
    }

    @GetMapping("/files")
    @ResponseBody
    public ArrayList<Map<String, Object>> files(@RequestParam(value = "fileId", required = false) final String fileId) {  // get files information
        return fileId == null ? documentManager.getFilesInfo() : documentManager.getFilesInfo(fileId);
    }

    @PostMapping(path = "${url.track}")
    @ResponseBody
    public String track(final HttpServletRequest request,  // track file changes
                        @RequestParam("fileId") final String fileId,
                        @RequestParam("userAddress") final String userAddress,
                        @RequestBody final Callback body) {
        CallbackUtils.log(body);
        Callback callback;
        try {
            String bodyString = objectMapper
                    .writeValueAsString(body);  // write the request body to the object mapper as a string

            if (bodyString.isEmpty()) {  // if the request body is empty, an error occurs
                throw new RuntimeException("{\"error\":1,\"message\":\"Request payload is empty\"}");
            }

            String authorizationHeader = request.getHeader(settingsManager.getSecurityHeader());
            callback = callbackService.verifyCallback(body, authorizationHeader);

            callbackService.processCallback(callback, fileId);
        } catch (Exception e) {
            String message = e.getMessage();
            if (!message.contains("\"error\":1")) {
                e.printStackTrace();
            }
            return message;
        }

        return "{\"error\":\"0\"}";
    }

    @PostMapping("/saveas")
    @ResponseBody
    public String saveAs(@RequestBody final SaveAs body, @CookieValue("uid") final String uid) {
        throw new RuntimeException("Not implemented");
       /* try {
            String fileName = documentManager.getCorrectName(body.getTitle());

            if (documentManager.getDocumentType(fileName) == null) {
                return "{\"error\":\"File type is not supported\"}";
            }

            String url = body.getUrl();

            url = urlManager.replaceToInnerDocumentServerUrl(url);

            return requestManager.executeGetRequest(url, new RequestManager.Callback<String>() {
                @Override
                public String doWork(final Object response) throws Exception {
                    InputStream stream = ((HttpEntity) response).getContent();

                    if (documentManager.getMaxFileSize() < stream.available() || stream.available() <= 0) {
                        return "{\"error\":\"File size is incorrect\"}";
                    }
                    storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(fileName)), stream);
                    createUserMetadata(uid, fileName);

                    return "{\"file\":  \"" + fileName + "\"}";
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            return "{ \"error\" : 1, \"message\" : \"" + e.getMessage() + "\"}";
        }*/
    }

    /**
     * 文件重命名
     *
     * @param body
     * @return
     */
    @PostMapping("/rename")
    @ResponseBody
    public String rename(@RequestBody final Rename body) {
        limitUtil.validateOp("rename");
        String fileId = body.getFileId();
        OfficeFile officeFile = officeFileServices.getById(fileId);
        if (officeFile == null) {
            OfficeException.valueOf("文件不存在,fileId=" + fileId);
        }
        String title = body.getFileName() + "." + body.getFileType();
        CommandRequest commandRequest = CommandRequest.builder()
                .key(body.getFileKey())
                .c(Command.META)
                .meta(Meta.builder()
                        .title(title)
                        .build())
                .build();
        try {
            officeFile.setTitle(title);
            officeFileServices.save(officeFile);
            CommandResponse commandResponse = commandService.processCommand(commandRequest, officeFile.getId());
            return commandResponse.getError().getDescription();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

    /**
     * 未处理逻辑
     *
     * @param body
     * @return
     */
    @PostMapping("/reference")
    @ResponseBody
    public String reference(@RequestBody final Reference body) {
        throw OfficeException.valueOf("未处理逻辑");
        /*try {
            JSONParser parser = new JSONParser();
            Gson gson = new GsonBuilder().disableHtmlEscaping().create();

            String userAddress = "";
            String fileName = "";

            if (body.getReferenceData() != null) {
                ReferenceData referenceData = body.getReferenceData();

                if (referenceData.getInstanceId().equals(storagePathBuilder.getServerUrl(false))) {
                    JSONObject fileKey = (JSONObject) parser.parse(referenceData.getFileKey());
                    userAddress = (String) fileKey.get("userAddress");
                    if (userAddress.equals(InetAddress.getLocalHost().getHostAddress())) {
                        fileName = (String) fileKey.get("fileName");
                    }
                }
            }

            String link = body.getLink();
            if (fileName.equals("") && link != null) {
                if (!link.contains(storagePathBuilder.getServerUrl(true))) {
                    HashMap<String, String> data = new HashMap<>();
                    data.put("url", link);
                    return gson.toJson(data);
                }

                UriComponents uriComponents = UriComponentsBuilder.fromUriString(body.getLink()).build();
                fileName = uriComponents.getQueryParams().getFirst("fileName");
                boolean fileExists = new File(storagePathBuilder.getFileLocation(fileName)).exists();
                if (!fileExists) {
                    return "{ \"error\": \"File is not exist\"}";
                }
            }

            if (fileName.equals("")) {
                try {
                    String path = (String) body.getPath();
                    path = path.substring(path.lastIndexOf('/') + 1);
                    path = path.split("\\?")[0];
                    File f = new File(storagePathBuilder.getFileLocation(path));
                    if (f.exists()) {
                        fileName = path;
                    }
                } catch (Exception e) {
                    return "{ \"error\" : 1, \"message\" : \"" + e.getMessage() + "\"}";
                }
            }

            if (fileName.equals("")) {
                return "{ \"error\": \"File not found\"}";
            }

            HashMap<String, Object> fileKey = new HashMap<>();
            fileKey.put("fileName", fileName);
            fileKey.put("userAddress", InetAddress.getLocalHost().getHostAddress());

            HashMap<String, Object> referenceData = new HashMap<>();
            referenceData.put("instanceId", storagePathBuilder.getServerUrl(true));
            referenceData.put("fileKey", gson.toJson(fileKey));

            HashMap<String, Object> data = new HashMap<>();
            data.put("fileType", documentManager.getDocumentName(fileName));
            data.put("key", documentManager.generateRevisionId(
                    storagePathBuilder.getStorageLocation()
                            + "/" + fileName + "/"
                            + new File(storagePathBuilder.getFileLocation(fileName)).lastModified()
            ));
            data.put("url", urlManager.getFileUrl(fileName));
            data.put("referenceData", referenceData);
            data.put("path", fileName);
            data.put("link", storagePathBuilder.getServerUrl(true) + "/editor?fileName=" + fileName);

            if (settingsManager.isSecurityEnabled()) {
                String token = jwtManager.createToken(data);
                data.put("token", token);
            }
            return gson.toJson(data);
        } catch (Exception e) {
            e.printStackTrace();
            return "{ \"error\" : 1, \"message\" : \"" + e.getMessage() + "\"}";
        }*/
    }

    @GetMapping("/history")
    @ResponseBody
    public String history(@RequestParam("fileId") final String fileId) {
        limitUtil.validateOp("history");
        return historyManager.getHistory(fileId);
    }

    @GetMapping("/historydata")
    @ResponseBody
    public String history(@RequestParam("fileId") final String fileId,
                          @RequestParam("version") final String version) {
        limitUtil.validateOp("historydata");
        return historyManager.getHistoryData(fileId, version);
    }

    @PutMapping("/restore")
    @ResponseBody
    public String restore(@RequestBody final Restore body, @CookieValue("uid") final Integer uid) {
        limitUtil.validateOp("restore");
        String fileId = body.getFileId();
        if (StrUtil.isBlank(fileId)) {
            throw new RuntimeException("还原的 fileId 必须传递");
        }
        OfficeFile officeFile = officeFileServices.getById(fileId);
        if (officeFile == null) {
            throw new RuntimeException("restore File not found");
        }
        String fileName = officeFile.getName();
        try {
            //原始文件名称，现在数据库中对应的
            String sourceStringFile = storagePathBuilder.getFileLocation(officeFile, fileName);
            File sourceFile = new File(sourceStringFile);
            Path sourcePathFile = sourceFile.toPath();
            //获取历史根路径
            String historyDirectory = storagePathBuilder.getHistoryDir(sourcePathFile.toString());
            log.info("历史根路径  historyDirectory={}", historyDirectory);
            //碰撞版本，
            Integer bumpedVersion = storagePathBuilder.getFileVersion(historyDirectory, false);
            log.info("bumpedVersion={}", bumpedVersion);
            //开始备份版本目录 新版本的编号，比如一般如果下面最大的目录是2，则当前版本编号是3
            String bumpedVersionStringDirectory = historyManager.versionDir(historyDirectory, bumpedVersion, true);
            log.info("bumpedVersionStringDirectory={}", bumpedVersionStringDirectory);
            //  /data/documents/localhost/new.docx-hist/3
            File bumpedVersionDirectory = new File(bumpedVersionStringDirectory);
            if (!bumpedVersionDirectory.exists()) {
                bumpedVersionDirectory.mkdir();
            }
            // 创建   /data/documents/localhost/new.docx-hist/3/key.txt
            Path bumpedKeyPathFile = Paths.get(bumpedVersionStringDirectory, "key.txt");
            String bumpedKeyStringFile = bumpedKeyPathFile.toString();
            File bumpedKeyFile = new File(bumpedKeyStringFile);
            // String bumpedKey = fileId + "-" + System.currentTimeMillis();
            String bumpedKey = officeFile.getFileKey();
            try (FileWriter bumpedKeyFileWriter = new FileWriter(bumpedKeyFile)) {
                bumpedKeyFileWriter.write(bumpedKey);
            }

            //创建  /data/documents/localhost/new-hist/3/changes.json
            User user = userService.findUserById(uid);
            Path bumpedChangesPathFile = Paths.get(bumpedVersionStringDirectory, "changes.json");
            String bumpedChangesStringFile = bumpedChangesPathFile.toString();
            File bumpedChangesFile = new File(bumpedChangesStringFile);
            JSONObject bumpedChangesUser = new JSONObject();
            // Don't add the `uid-` prefix.
            // https://github.com/ONLYOFFICE/document-server-integration/issues/437#issuecomment-1663526562
            bumpedChangesUser.put("id", user.getId());
            bumpedChangesUser.put("name", user.getName());
            JSONObject bumpedChangesChangesItem = new JSONObject();
            bumpedChangesChangesItem.put("created", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            bumpedChangesChangesItem.put("user", bumpedChangesUser);
            JSONArray bumpedChangesChanges = new JSONArray();
            bumpedChangesChanges.add(bumpedChangesChangesItem);
            JSONObject bumpedChanges = new JSONObject();
            bumpedChanges.put("serverVersion", null);
            bumpedChanges.put("changes", bumpedChangesChanges);
            String bumpedChangesContent = bumpedChanges.toJSONString();
            try (FileWriter bumpedChangesFileWriter = new FileWriter(bumpedChangesFile)) {
                bumpedChangesFileWriter.write(bumpedChangesContent);
            }
            //创建文件进行备份  /data/documents/localhost/new.docx-hist/3/prev.docx
            String sourceExtension = documentManager.getExtension(fileName);
            String previousBasename = "prev." + sourceExtension;
            Path bumpedFile = Paths.get(bumpedVersionStringDirectory, previousBasename);
            //把原文件移走到bumpedFile的路径处
            Files.move(sourcePathFile, bumpedFile);
            //把选择的版本文件写入到正式的路径位置。
            if (body.getUrl() != null) {
                //进行文件下载
                java.net.HttpURLConnection connection = (java.net.HttpURLConnection) new URL(body.getUrl())
                        .openConnection();
                try {
                    storageMutator.createFileNotExists(sourcePathFile, connection.getInputStream());
                } catch (Exception e) {
                    log.error("e", e);
                } finally {
                    connection.disconnect();
                }
            } else {
                //进行本地文件的备份
                String recoveryVersionStringDirectory = historyManager.versionDir(
                        historyDirectory,
                        body.getVersion(),
                        true
                );
                // /data/documents/localhost/new.docx-hist/2/prev.docx
                // 比如选择2版本进行还原，把原文件的内容进行覆盖
                Path recoveryPathFile = Paths.get(recoveryVersionStringDirectory, previousBasename);
                String recoveryStringFile = recoveryPathFile.toString();
                //文件流会自动关闭
                storageMutator.createFileNotExists(sourcePathFile, new FileInputStream(recoveryStringFile));
            }
            //更新数据库中文件的key
            officeFile.setUpdateTime(LocalDateTime.now());
            officeFile.setFileKey(IdUtil.getFileKey(fileId));
            officeFileServices.save(officeFile);

            JSONObject responseBody = new JSONObject();
            responseBody.put("error", null);
            responseBody.put("success", true);
            return responseBody.toJSONString();
        } catch (Exception error) {
            error.printStackTrace();
            JSONObject responseBody = new JSONObject();
            responseBody.put("error", error.getMessage());
            responseBody.put("success", false);
            return responseBody.toJSONString();
        }
    }

    @GetMapping("/config")
    public ResponseEntity<RefreshConfig> config(@RequestParam("fileId") final String fileId,
                                                @RequestParam("permissions") final String permissions,
                                                @CookieValue(value = "uid", required = false) final String uid) {

        OfficeFile officeFile = officeFileServices.getById(fileId);
        if (officeFile == null) {
            throw new RuntimeException("fileId 没有传递");
        }
        try {
            String fileName = officeFile.getName();
            if (!new File(storagePathBuilder.getFileLocation(officeFile, fileName)).exists()) {
                throw (new Exception("File not found"));
            }
            Gson gson = new GsonBuilder().disableHtmlEscaping().create();
            Document document = new Document();
            document.setTitle(officeFile.getTitle());
            document.setKey(officeFile.getFileKey());
            document.setUrl(urlManager.getFileUrl(fileId));
            document.setReferenceData(configService.getReferenceData(fileId));
            document.setPermissions(gson.fromJson(permissions, new TypeToken<Map<String, Object>>() {
            }.getType()));

            EditorConfig editorConfig = new EditorConfig();
            editorConfig.setCallbackUrl(urlManager.getCallbackUrl(fileId));

            RefreshConfig config = new RefreshConfig();
            config.setDocument(document);
            config.setEditorConfig(editorConfig);
            if (settingsManager.isSecurityEnabled()) {
                config.setToken(jwtManager.createToken(config));
            }
            return ResponseEntity.ok(config);
        } catch (Exception e) {
            RefreshConfig error = new RefreshConfig();
            error.setError(e.getMessage());
            return ResponseEntity.internalServerError().body(error);
        }
    }
}
